<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>大文件分片上传示例</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.2/spark-md5.min.js"></script>
    <script>
        //vari = -1;
        var chunkSize = 5 * 1024 * 1024;    //以5MB为一个分片
        var succeed = 0;
        var currentIndex = 0;
        var shardCount = 0;
        var databgein;  //开始时间
        var dataend;    //结束时间
        var page = {
            init: function () {
                $("#upload").click(function () {
                    //清空
                    $("#usetime").text('');
                    $("#param").text('');
                    $("#output").text('');

                    databgein = new Date();
                    var file = $("#file")[0].files[0];  //文件对象
                    if (file == null) {
                        alert("文件不能为空");
                        return;
                    }
                    computeMD5(file);
                });

                $("#querybut").click(function () {
                    var json = {"page": 1, "limit": 10, "name": ""};
                    //Ajax提交
                    $.ajax({
                        url: "http://localhost:8080/file/selectFileList",
                        type: "POST",
                        data: JSON.stringify(json),
                        dataType: "json",
                        async: true,        //异步
                        contentType: "application/json;charset=utf-8",
                        success: function (data) {
                            $("#filediv").text("");
                            if (data.data.records != null) {
                                $(data.data.records).each(function () {
                                    var tr = $("#filediv").append("<tr></tr>");
                                    tr.append("<td>" + this.identifier + "</td>");
                                    tr.append("<td>" + this.filename + "</td>");
                                    tr.append("<td>" + this.location + "</td>");
                                })
                            }

                        }, error: function (XMLHttpRequest, textStatus, errorThrown) {
                            alert("服务器出错!");
                        }

                    });
                });
            }
        };
        $(function () {
            page.init();
        });

        /**
         * 计算md5，实现断点续传及秒传
         * @param file
         */
        function computeMD5(file) {
            let fileReader = new FileReader();
            let time = new Date().getTime();
            let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
            let currentChunk = 0;
            let chunks = Math.ceil(file.size / chunkSize);
            let spark = new SparkMD5.ArrayBuffer();

            loadNext();

            fileReader.onload = (e => {

                spark.append(e.target.result);

                if (currentChunk < chunks) {
                    currentChunk++;
                    loadNext();
                    // console.log('校验MD5 ' + ((currentChunk / chunks) * 100).toFixed(0) + '%');
                    $("#md5span").text(+((currentChunk / chunks) * 100).toFixed(0) + '%');

                } else {
                    let md5 = spark.end();

                    console.log(`MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${file.size} 用时：${new Date().getTime() - time} ms`);

                    isUpload(file, md5);
                }
            });

            function loadNext() {
                let start = currentChunk * chunkSize;
                let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

                fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
            }
        }

        function isUpload(file, md5) {
            //Ajax提交
            $.ajax({
                url: "http://localhost:8080/file/upload?" + "identifier=" + md5,
                type: "GET",
                async: true,        //异步
                processData: false,  //很重要，告诉jquery不要对form进行处理
                contentType: false,  //很重要，指定为false才能形成正确的Content-Type
                success: function (data) {

                    // 服务器分片校验函数，秒传及断点续传基础
                    if (data.data.skipUpload) {
                        dataend = new Date();
                        $("#usetime").text(dataend.getTime() - databgein.getTime());
                        $("#rate").text("100");
                        $("#output").text(shardCount + " / " + shardCount);
                    } else {
                        repeatupload(file, md5, data.data.uploadedChunks);
                    }

                }, error: function (XMLHttpRequest, textStatus, errorThrown) {
                    alert("服务器出错!");
                }

            });


        }

        function repeatupload(file, filemd5, uploadedChunks) {
            size = file.size;        //总大小
            shardCount = Math.ceil(size / chunkSize);  //总片数

            for (var i = 0; i < shardCount; i++) {
                var chunkNumber = i + 1;
                if (uploadedChunks != null && uploadedChunks.indexOf(chunkNumber) >= 0) {
                    console.log(chunkNumber + "分片已存在");
                    //如果分片存在就不用上传了
                    uploadChunks(file.name, filemd5, size);
                    continue;
                }
                upload(file, filemd5, uploadedChunks, chunkNumber);
            }
        }

        /*
         *上传每一分片
         * file 文件对象
         * filemd5 整个文件的md5
         * date  文件第一个分片上传的日期(如:20170122)
         * i  文件第i个分片
         * type  1为检测；2为上传
         */
        function upload(file, filemd5, uploadedChunks, chunkNumber) {
            //计算每一片的起始与结束位置
            var start = (chunkNumber - 1) * chunkSize,
                end = Math.min(size, start + chunkSize);
            //构造一个表单，FormData是HTML5新增的
            var form = new FormData();
            //按大小切割文件段　　
            var data = file.slice(start, end);

            form.append("chunkNumber", chunkNumber);  //文件块编号，从1开始

            form.append("totalChunks", shardCount);
            form.append("identifier", filemd5);
            form.append("chunkSize", chunkSize);
            form.append("currentChunkSize", data.size);
            form.append("relativePath", file.name);
            form.append("filename", file.name);
            form.append("totalSize", size);
            form.append("total", shardCount);  //总片数
            form.append("upfile", data);

            //Ajax提交
            $.ajax({
                url: "http://localhost:8080/file/upload",
                type: "POST",
                data: form,
                async: true,        //异步
                processData: false,  //很重要，告诉jquery不要对form进行处理
                contentType: false,  //很重要，指定为false才能形成正确的Content-Type
                success: function (data) {
                    uploadChunks(file.name, filemd5, size);

                }, error: function (XMLHttpRequest, textStatus, errorThrown) {
                    alert("服务器出错!");
                }

            });


        }

        function uploadChunks(filename, identifier, totalSize) {
            //服务器返回分片是否上传成功
            ++succeed;//改变界面
            if (succeed > shardCount) {
                succeed = shardCount;
            }

            $("#output").text(succeed + " / " + shardCount);

            if (succeed == shardCount) {
                merge(filename, identifier, totalSize);

                dataend = new Date();
                $("#usetime").text(dataend.getTime() - databgein.getTime());
            }


            //进度
            ++currentIndex;

            if (currentIndex > shardCount) {
                currentIndex = shardCount;
            }
            $("#rate").text(((currentIndex / shardCount) * 100).toFixed(0));
        }

        function merge(filename, identifier, totalSize) {
            var json = {"filename": filename, "identifier": identifier, "totalSize": totalSize};
            //Ajax提交
            $.ajax({
                url: "http://localhost:8080/file/merge",
                type: "POST",
                data: JSON.stringify(json),
                dataType: "json",
                async: true,        //异步
                contentType: "application/json;charset=utf-8",
                success: function (data) {
                    if (data.code != 200) {
                        alert("服务器出错!");
                    }

                }, error: function (XMLHttpRequest, textStatus, errorThrown) {
                    alert("服务器出错!");
                }

            });


        }

    </script>
</head>
<body>

<input id="file" type="file"/>
<button id="upload">上传</button>
<span style="font-size:12px">校验MD5: <span id="md5span"></span></span>
<span style="font-size:12px;margin-left:20px;">等待: <span id="output"></span></span>
<span style="font-size:12px;margin-left:20px;">进度: <span id="rate"></span>%</span>
<span style="font-size:12px;margin-left:20px;">用时: <span id="usetime"></span></span>

<br/>
<br/>
<br/>
<br/>
<button id="querybut">查询</button>
<h2>文件列表</h2>
<table>
    <thead>
    <tr>
        <td>md5</td>
        <td>文件名</td>
        <td>地址</td>
    </tr>
    </thead>
    <tbody id="filediv"></tbody>
</table>


</body>

</html>
